﻿using FileWatcher;
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace ModernUILogViewer.ViewModel
{
    public class TailLogViewModel : BaseViewModel
    {
        #region Constructor

        public TailLogViewModel(MainViewModel mainVM)
        {
            this.mainVM = mainVM;
            this.AutoScrollLastLine = true;
            this.Status = "Idle";
        }

        #endregion

        #region Fields

        MainViewModel mainVM;
        BaseWatcher watcher;
        const int WATCH_TAIL_EXECUTING_DELAY = 1000;
        CancellationTokenSource tokenSource = new CancellationTokenSource();        

        #endregion

        #region Events

        public event EventHandler ClearTextRequestedEvent;

        #endregion

        #region Properties

        public Action<string> AppendTextAction { get; set; }

        private string status;

        public string Status
        {
            get { return status; }
            set { 
                status = value;
                RaisePropertyChanged(() => Status);
            }
        }

        private long downloadedContentLength;

        public long DownloadedContentLength
        {
            get { return downloadedContentLength; }
            set { 
                downloadedContentLength = value;
                RaisePropertyChanged(() => DownloadedContentLength);
            }
        }
        

        private DateTime lastModified;

        public DateTime LastModified
        {
            get { return lastModified; }
            set {                
                lastModified = value;
                RaisePropertyChanged(() => LastModified);
            }
        }
        

        private bool isProcessing;

        public bool IsProcessing
        {
            get { return isProcessing; }
            set
            {
                isProcessing = value;
                RaisePropertyChanged(() => IsProcessing);
            }
        }

        private string filename;

        public string Filename
        {
            get { return filename; }
            set { 
                filename = value;
                RaisePropertyChanged(() => Filename);
            }
        }

        private bool autoScrollLastLine;

        public bool AutoScrollLastLine
        {
            get { return autoScrollLastLine; }
            set { 
                autoScrollLastLine = value;
                RaisePropertyChanged(() => AutoScrollLastLine);
            }
        }
        

        private string text;

        public string Text
        {
            get { return text; }
            set { 
                text = value;
                RaisePropertyChanged(() => Text);
            }
        }        

        #endregion

        #region Methods

        void ToggleAutoScrollLastLineExecute()
        {
            this.AutoScrollLastLine = !autoScrollLastLine;
        }

        void OpenLogFileDialogExecute()
        {
            string[] files = ShowOpenFilesDlg(false);

            string logfile = files.Length > 0 ? files[0] : null;

            if (logfile != null)
            {
                //logfile = "http://aixws7test.inps.it/APPLICAZIONI/PSR_CIGODomandaAPP/logs/PsrCigodomanda.log";

                this.filename = logfile;
                RaisePropertyChanged(() => Filename);                
            }

        }

        void StopTailExecute()
        {
            Status = "Stop requested";

            tokenSource.Cancel();
        }

        void TailFile()
        {
            bool isFirstTime = true;

            Task.Factory.StartNew(() =>
            {
                IsProcessing = true;

                StringBuilder sb = new StringBuilder();

                while (true)
                {
                    if (tokenSource.IsCancellationRequested)
                    {
                        IsProcessing = false;
                        Status = "Idle";
                        break;
                    }                        

                    Status = "Running...";

                    Stream fileData = null;

                    try
                    {
                        if (isFirstTime)
                        {
                            int initialLimit = mainVM.FileWatcherOptionsVM.InitialLimit * 1000;
                            if (initialLimit < 0)
                                initialLimit = 0;
                            watcher.Limit = initialLimit;
                            isFirstTime = false;
                        }
                        else
                            watcher.Limit = 0;

                        fileData = watcher.GetLatest();
                    }
                    catch (Exception e)
                    {
                        Status = e.Message;
                        tokenSource.Cancel();
                    }

                    if (fileData != null)
                    {
                        LastModified = watcher.LastModifiedSince;

                        DownloadedContentLength = watcher.DownloadedContentLength;

                        Status = "Updating...";

                        using (StreamReader fileReader = new StreamReader(fileData))
                        {
                            if (fileReader.Peek() > 0)
                            {
                                do
                                {
                                    sb.Append(fileReader.ReadLine());
                                    sb.Append(Environment.NewLine);
                                }
                                while (!fileReader.EndOfStream);
                            }
                        }

                        if (RootDispatcher.CheckAccess())
                        {
                            // do work on UI thread
                            AppendTextAction.Invoke(sb.ToString());
                        }
                        else
                        {
                            // or BeginInvoke()
                            RootDispatcher.Invoke((Action)delegate()
                            {
                                AppendTextAction.Invoke(sb.ToString());
                            });
                        }

                        sb.Clear();
                    }

                    Thread.Sleep(WATCH_TAIL_EXECUTING_DELAY);
                }
            }).ContinueWith((result) =>
            {
                IsProcessing = false;
                Status = "Idle";

            }, tokenSource.Token);
        }

        void StartTailExecute()
        {
            tokenSource = new CancellationTokenSource();

            // Register the callback when token is cancelled
            tokenSource.Token.Register(() =>
            {
                //IsProcessing = false;
            });

            try
            {
                // clear text
                RaiseEventInvoker(ClearTextRequestedEvent);

                Text = String.Empty;

                LastModified = default(DateTime);

                // create file watcher
                watcher = FileWatcherFactory.Create(filename);

                if (watcher.UriType == UriType.REMOTE)
                {
                    FileWatcherOptionsViewModel nfwVM = mainVM.FileWatcherOptionsVM;

                    RemoteWatcher rw = (RemoteWatcher)watcher;
                    ICredentials credentials = null;
                    IWebProxy proxy = null;

                    if (nfwVM.EnableCredentials)
                    {
                        credentials = new NetworkCredential(nfwVM.User, nfwVM.Password, nfwVM.Domain);
                        rw.Credentials = credentials;
                    }
                    if (nfwVM.EnableProxy)
                    {
                        proxy = new WebProxy(nfwVM.Host, nfwVM.Port);
                        if (credentials != null)
                            proxy.Credentials = credentials;
                        rw.Proxy = proxy;
                    }
                }

                TailFile();
            }
            catch (Exception e)
            {
                Status = e.Message;
            }

            
        }

        void ClearContentExecute()
        {
            RaiseEventInvoker(ClearTextRequestedEvent);
        }

        #endregion

        #region Commands

        public ICommand OpenLogFileDialogCommand { get { return new MvvmFoundation.Wpf.RelayCommand(OpenLogFileDialogExecute); } }
        //public ICommand OpenFileCommand { get { return new MvvmFoundation.Wpf.RelayCommand(OpenFileExecute); } }
        public ICommand ToggleAutoScrollLastLineCommand { get { return new MvvmFoundation.Wpf.RelayCommand(ToggleAutoScrollLastLineExecute); } }
        public ICommand StartTailCommand { get { return new MvvmFoundation.Wpf.RelayCommand(StartTailExecute); } }
        public ICommand StopTailCommand { get { return new MvvmFoundation.Wpf.RelayCommand(StopTailExecute); } }
        public ICommand ClearContentCommand { get { return new MvvmFoundation.Wpf.RelayCommand(ClearContentExecute); } }        

        #endregion
    }
}
